use crate::controller::Error;
use crate::service::ServiceSet;
use crate::{dto, mq};
use std::sync::Arc;

pub struct TrafficSteeringRuleController {
    service_set: Arc<ServiceSet>,
    event_handler: EventHandler,
}

impl TrafficSteeringRuleController {
    pub fn new(service_set: Arc<ServiceSet>, mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self {
            service_set,
            event_handler: EventHandler::new(mq_client_set),
        }
    }
    pub async fn add(
        &self,
        request: &dto::traffic_steering_rule::AddRequest,
    ) -> Result<dto::core::traffic_steering_rule::AddResponseData, Error> {
        let endpoint = self
            .service_set
            .endpoint_manager
            .get(request.endpoint_id.as_str())
            .await?;

        let data = self.service_set.traffic_steering_rule.add(request).await?;

        self.event_handler.spawn_handle_setup_event(
            endpoint.inner.node_id.as_str(),
            request.endpoint_id.as_str(),
            data.traffic_steering_rule_id.as_str(),
        );

        Ok(data)
    }

    pub async fn list(
        &self,
        request: &dto::traffic_steering_rule::ListRequest,
    ) -> Result<dto::traffic_steering_rule::ListResponseData, Error> {
        let total_count = self
            .service_set
            .traffic_steering_rule
            .count(&request.filter)
            .await?;

        if total_count == 0 {
            return Ok(dto::traffic_steering_rule::ListResponseData {
                total_count: 0,
                list: Vec::new(),
            });
        }

        let list = self
            .service_set
            .traffic_steering_rule
            .list(&request.filter, request.offset, request.limit)
            .await?;

        Ok(dto::traffic_steering_rule::ListResponseData { total_count, list })
    }

    pub async fn get(
        &self,
        request: &dto::traffic_steering_rule::GetRequest,
    ) -> Result<dto::core::traffic_steering_rule::TrafficSteeringRuleItem, Error> {
        let data = self
            .service_set
            .traffic_steering_rule
            .get(request.traffic_steering_rule_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn update(
        &self,
        request: &dto::traffic_steering_rule::UpdateRequest,
    ) -> Result<(), Error> {
        if request.params == Default::default() {
            return Ok(());
        }

        self.service_set
            .traffic_steering_rule
            .update(request.traffic_steering_rule_id.as_str(), &request.params)
            .await?;

        Ok(())
    }

    pub async fn delete(
        &self,
        request: &dto::traffic_steering_rule::DeleteRequest,
    ) -> Result<(), Error> {
        let rule = self
            .service_set
            .traffic_steering_rule
            .get(request.traffic_steering_rule_id.as_str())
            .await?;

        let endpoint = self
            .service_set
            .endpoint_manager
            .get(rule.inner.endpoint_id.as_str())
            .await?;

        self.service_set
            .traffic_steering_rule
            .delete(request.traffic_steering_rule_id.as_str())
            .await?;

        self.event_handler.spawn_handle_delete_event(
            endpoint.inner.node_id.as_str(),
            rule.inner.endpoint_id.as_str(),
            request.traffic_steering_rule_id.as_str(),
        );

        Ok(())
    }
}
pub struct EventHandler {
    mq_client_set: Arc<mq::client::ClientSet>,
}

impl EventHandler {
    pub fn new(mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self { mq_client_set }
    }
    fn spawn_handle_setup_event(
        &self,
        node_id: &str,
        endpoint_id: &str,
        traffic_steering_rule_id: &str,
    ) {
        let mq_client_set = self.mq_client_set.clone();
        let node_id = node_id.to_string();
        let endpoint_id = endpoint_id.to_string();
        let traffic_steering_rule_id = traffic_steering_rule_id.to_string();
        tokio::spawn(async move {
            if let Err(e) = mq_client_set
                .node
                .endpoint
                .traffic_steering_rule
                .publish_setup_event(
                    node_id.as_str(),
                    dto::event::endpoint::traffic_steering_rule::SetupEventPayload {
                        endpoint_id: endpoint_id.clone(),
                        traffic_steering_rule_id: traffic_steering_rule_id.clone(),
                    },
                )
                .await
            {
                log::error!("{:?}", e);
            }
        });
    }

    fn spawn_handle_delete_event(
        &self,
        node_id: &str,
        endpoint_id: &str,
        traffic_steering_rule_id: &str,
    ) {
        let mq_client_set = self.mq_client_set.clone();
        let node_id = node_id.to_string();
        let endpoint_id = endpoint_id.to_string();
        let traffic_steering_rule_id = traffic_steering_rule_id.to_string();

        tokio::spawn(async move {
            if let Err(e) = mq_client_set
                .node
                .endpoint
                .traffic_steering_rule
                .publish_delete_event(
                    node_id.as_str(),
                    dto::event::endpoint::traffic_steering_rule::DeleteEventPayload {
                        endpoint_id: endpoint_id.clone(),
                        traffic_steering_rule_id: traffic_steering_rule_id.clone(),
                    },
                )
                .await
            {
                log::error!("{:?}", e);
            }
        });
    }
}
